//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<RenderContext.cpp>
///	@path	~/src/graphics/
///	@date	2007/10/25
///	@desc	.

#include "config/config.h"

#include "lib/system/thread_debug.h"

#include "graphics/RenderContext.h"
#include "graphics/dx9/CDevice9.h"
#include "graphics/dx9/CRenderer9.h"
#include "graphics/dx10/CDevice10.h"
#include "graphics/dx10/CRenderer10.h"

namespace xeres {

	// ctor
	RenderContext::RenderContext( void )
		: m_device( NULL ) , m_renderer( NULL )
		, m_device9( NULL ) , m_device10( NULL )
		, m_renderer9( NULL ) , m_renderer10( NULL )
		, m_dx9( NULL ) , m_dx10( NULL )
		// info
		, m_xres_i( 0 ) , m_yres_i( 0 )
		, m_xres_f( 0 ) , m_yres_f( 0 )
	{
		m_currentViewProj.identity();
	}

	// dtor
	RenderContext::~RenderContext( void )
	{
	}

	// ToggleFullScreen
	void RenderContext::ToggleFullScreen( void )
	{
		m_device->ToggleFullScreen( NULL );
	}

	// Setup
	void RenderContext::Setup( RefWeak<IDevice> device , RefWeak<IRenderer> renderer )
	{
		m_device = device;
		m_renderer = renderer;
		if( m_device.IsValid() && m_renderer.IsValid() )
		{
			if( m_device->IsA<CDevice9>() )
			{
				m_device9 = m_device;
				assert( m_renderer->IsA<CRenderer9>() );
				m_renderer9 = m_renderer;
				m_dx9 = m_device9->GetDevice();
				m_device9->GetResolution( m_xres_i , m_yres_i );
				m_xres_f = (float)m_xres_i;
				m_yres_f = (float)m_yres_i;
			}
			else if( m_device->IsA<CDevice10>() )
			{
				m_device10 = m_device;
				assert( m_renderer->IsA<CRenderer10>() );
				m_renderer10 = m_renderer;
				m_dx10 = m_device10->GetDevice();
				m_device10->GetResolution( m_xres_i , m_yres_i );
				m_xres_f = (float)m_xres_i;
				m_yres_f = (float)m_yres_i;
			}
			else
			{
				FATAL_ABORT( _S("Only DX9 or DX10 device is available.") );
			}
			Resized();
		}
	}
	// Initialize
	void RenderContext::Initialize( void )
	{
	}
	// Finalize
	void RenderContext::Finalize( void )
	{
	}

	// device size change
	void RenderContext::DeviceOnSize( void * window , bool miminized )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("device") );

		if( m_device.IsValid() )
		{
			m_device->OnSize( miminized );
			Resized();
		}
	}

	// DeviceOnEnterSizing
	void RenderContext::DeviceOnEnterSizing( void * window )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("device") );

		if( m_device.IsValid() )
		{
			m_device->EnterSizing();
		}
	}

	// DeviceOnExitSizing
	void RenderContext::DeviceOnExitSizing( void * window )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("device") );

		if( m_device.IsValid() )
		{
			m_device->ExitSizing();
		}
		Resized();
	}

	// Resized
	void RenderContext::Resized( void )
	{
		int xres = ResX<int>() , yres = ResY<int>();
		if( m_device->IsA<CDevice9>() )
		{
			RefWeak<CDevice9> d9 = m_device;
			d9->GetResolution( xres , yres );
		}
		else
		{
			RefWeak<CDevice10> d10 = m_device;
			d10->GetResolution( xres , yres );
		}
		m_xres_f = (float)( m_xres_i = xres );
		m_yres_f = (float)( m_yres_i = yres );
		m_inv_xres_f = 1 / m_xres_f;
		m_inv_yres_f = 1 / m_yres_f;
		m_xres_f2 = m_xres_f / 2;
		m_yres_f2 = m_yres_f / 2;
		m_inv_xres_f2 = 1 / m_xres_f2;
		m_inv_yres_f2 = 1 / m_yres_f2;
	}

	// ProcessRasterCoord
	void RenderContext::ProcessRasterCoord( void * vertex , size_t stride , size_t count )
	{
		if( GetDX10() )
		{
			// only need to process coords in dx10
			byte * data = (byte*)vertex;
			for( size_t i = 0 ; i < count ; ++i , data += stride )
			{
				float * p = (float*)data;
				p[0] = p[0] * m_inv_xres_f2 - 1.f;
				p[1] = p[1] * (-m_inv_yres_f2) + 1.f;
			}
		}
	}

	// ProcessRasterCoord
	void RenderContext::ProcessRasterCoord( void * vertex , size_t stride , size_t count , float invx2 , float invy2 )
	{
		if( GetDX10() )
		{
			// only need to process coords in dx10
			byte * data = (byte*)vertex;
			for( size_t i = 0 ; i < count ; ++i , data += stride )
			{
				float * p = (float*)data;
				p[0] = p[0] * invx2 - 1.f;
				p[1] = p[1] * (-invy2) + 1.f;
			}
		}
	}

	// FixRasterTextureCoord
	void RenderContext::FixRasterTextureCoord( void * vertex , float w , float h , size_t offset , size_t stride , size_t count )
	{
		if( GetDX9() )
		{
			float dw = float( 1.0 / ( double(w) * 0.5 ) );
			float dh = float( 1.0 / ( double(h) * 0.5 ) );
			byte * data = (byte*)vertex;
			for( size_t i = 0 ; i < count ; ++i , data += stride )
			{
				float * p = (float*)(data+offset);
				p[0] += dw;
				p[1] += dh;
			}
		}
	}

	// GetRasterTextureCoord
	Pair<float> RenderContext::GetRasterTextureCoord( float w , float h )
	{
		if( GetDX9() )
		{
			float dw = 1.0f / w * 0.5f;
			float dh = 1.0f / h * 0.5f;
			return Pair<float>(dw,dh);
		}
		else
		{
			return Pair<float>(0,0);
		}
	}

	// SetViewport
	void RenderContext::SetViewport( uint x , uint y , uint w , uint h , float minz , float maxz )
	{
		if( GetDX9() )
		{
			D3DVIEWPORT9 v9 = { x , y , w , h , minz , maxz };
			GetDX9()->SetViewport( &v9 );
		}
		else
		{
			D3D10_VIEWPORT vp = { x , y , w , h , minz , maxz };
			GetDX10()->RSSetViewports( 1 , &vp );
		}
	}

	// GetViewport
	void RenderContext::GetViewport( Rect<int>& rect , float& minz , float& maxz )
	{
		if( GetDX9() )
		{
			D3DVIEWPORT9 v9;
			GetDX9()->GetViewport( &v9 );
			rect = make_rect<int>( v9.X , v9.Y , v9.Width , v9.Height );
			minz = v9.MinZ;
			maxz = v9.MaxZ;
		}
		else
		{
			uint viewports = 1;
			D3D10_VIEWPORT vp;
			GetDX10()->RSGetViewports( &viewports , &vp );
			rect = make_rect<int>( vp.TopLeftX , vp.TopLeftY , vp.Width , vp.Height );
			minz = vp.MinDepth;
			maxz = vp.MaxDepth;
		}
	}

	// RestoreViewport
	void RenderContext::RestoreViewport( void )
	{
		SetViewport( 0 , 0 , m_xres_i , m_yres_i , 0 , 1 );
	}

	// clear color and depth
	void RenderContext::ClearColorAndDepth( const PackColor4& color , float depth )
	{
		if( m_dx9 )
		{
			m_dx9->Clear( 0 , NULL , D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER ,
				color.get_packed_argb() , depth , 0 );
		}
		else
		{
			Color4 c = color.get_unpacked();
			ID3D10RenderTargetView * pRTV = NULL;
			ID3D10DepthStencilView * pDSV = NULL;
			m_dx10->OMGetRenderTargets( 1 , &pRTV , &pDSV );
			m_dx10->ClearRenderTargetView( pRTV , c.c );
			m_dx10->ClearDepthStencilView( pDSV , D3D10_CLEAR_DEPTH , depth , 0 );
			pRTV->Release();
			pDSV->Release();
		}
	}

	// clear color
	void RenderContext::ClearColor( const PackColor4& color )
	{
		if( m_dx9 )
		{
			m_dx9->Clear( 0 , NULL , D3DCLEAR_TARGET , color.get_packed_argb() , 0 , 0 );
		}
		else
		{
			Color4 c = color.get_unpacked();
			ID3D10RenderTargetView * pRTV = NULL;
			ID3D10DepthStencilView * pDSV = NULL;
			m_dx10->OMGetRenderTargets( 1 , &pRTV , &pDSV );
			m_dx10->ClearRenderTargetView( pRTV , c.c );
			pRTV->Release();
			pDSV->Release();
		}
	}

	// clear depth
	void RenderContext::ClearDepth( float depth )
	{
		if( m_dx9 )
		{
			m_dx9->Clear( 0 , NULL , D3DCLEAR_ZBUFFER , 0 , depth , 0 );
		}
		else
		{
			ID3D10DepthStencilView * pDSV = NULL;
			m_dx10->OMGetRenderTargets( 0 , NULL , &pDSV );
			m_dx10->ClearDepthStencilView( pDSV , D3D10_CLEAR_DEPTH , depth , 0 );
			pDSV->Release();
		}
	}
	// SetRenderPass
	void RenderContext::SetRenderPass( RefWeak<IPass> pass )
	{
		m_currentPass = pass;
		m_currentView = pass->GetPassView();
		Matrix<float> view = m_currentView->GetTransform();
		Matrix<float> proj = m_currentView->GetProjection();
		dx9::D3DXMatrixMultiply( (D3DXMATRIX*)m_currentViewProj.m16 ,
			(D3DXMATRIX*)view.m16 , (D3DXMATRIX*)proj.m16 );
	}
	// BeginScene
	void RenderContext::BeginScene( void )
	{
		if( m_dx9 )
		{
			m_dx9->BeginScene();
			m_renderer9->RestoreDefaultState();
		}
		else
		{
			(m_dx10);
		}
	}
	// EndScene
	void RenderContext::EndScene( void )
	{
		// clear states
		m_currentPass = NULL;
		m_currentView = NULL;
		m_currentViewProj.identity();

		// final call
		if( m_dx9 )
		{
			m_dx9->EndScene();
		}
		else
		{
			(m_dx10);
		}
	}
	
} // namespace xeres
